package com.tianji.learning.controller.service.impl;

import cn.hutool.db.DbRuntimeException;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.controller.service.ILearningLessonService;
import com.tianji.learning.controller.service.ILearningRecordService;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author zt
 * @since 2024-09-08
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final LearningRecordMapper learningRecordMapper;
    private final ILearningLessonService lessonService;
    private final CourseClient courseClient;
    private final LearningRecordDelayTaskHandler taskHandler;
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        //1.获取登录用户id
        Long userId = UserContext.getUser();
        //2.查询课表
        LearningLesson lesson = lessonService.queryByUserAndCourseId(userId,courseId);
        //3.查询学习记录
        //select * from xx where lession_id = #{lessionId}
        List<LearningRecord> records = this.lambdaQuery()
                                        .eq(LearningRecord::getLessonId, lesson.getId())
                                        .list();
        //4.封装结果
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        dto.setRecords(BeanUtils.copyList(records, LearningRecordDTO.class));
        return dto;
    }

    @Override
    @Transactional
    public void addLearningRecord(LearningRecordFormDTO recordDTO) {
//        //1.获取登录用户
//        Long userId = UserContext.getUser();
//        //2.处理学习记录
//        boolean finished = false;
//        if(recordDTO.getSectionType() == SectionType.VIDEO){
//            //2.1处理视频
//            finished = handleVideoRecord(userId,recordDTO);
//        }else {
//            //2.2处理考试
//            finished = handleExamRecord(userId,recordDTO);
//        }
//        if(!finished){
//            //没有新学完的小节，无需更新课表中的学习进度
//            return;
//        }
//        //3.处理课表数据，用finished判断是否要更新课表中已经学习的小节数量
//        handleLearningLessonChanges(recordDTO,finished);

        //1.获取用户id
        Long userId = UserContext.getUser();
        boolean finished = false;
        //2.处理学习记录
        if(recordDTO.getSectionType()== SectionType.VIDEO){
            //处理视频
            //1.先从缓存中查询
            LearningRecord old = taskHandler.readRecordCache(recordDTO.getLessonId(), recordDTO.getSectionId());
            if(old==null){
                //2.如果未命中，则要从数据库中查询
                old = this.lambdaQuery()
                        .eq(LearningRecord::getLessonId, recordDTO.getLessonId())
                        .eq(LearningRecord::getSectionId, recordDTO.getSectionId())
                        .one();
                //3.将数据库中查询到的结果写入缓存
                taskHandler.writeRecordCache(old);
            }
            if(old==null){
                //4.数据库里面也不存在，说明是第一次新增，要新增学习记录
                LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
                record.setUserId(userId);
                boolean success = this.save(record);
                if(!success){
                    throw new DbException("新增记录失败");
                }
                finished = false;
            }
            finished = !old.getFinished() && recordDTO.getMoment()*2 > recordDTO.getDuration();
            if(!finished){
                //不是第一次完成，只用更新redis就行
                LearningRecord record = new LearningRecord();
                record.setLessonId(recordDTO.getLessonId());
                record.setSectionId(recordDTO.getSectionId());
                record.setMoment(recordDTO.getMoment());
                record.setId(old.getId());
                record.setFinished(old.getFinished());
                taskHandler.addLearningRecordTask(record);
            }else{
                //是第一次完成，更新数据库同时清除redis缓存
                boolean success = this.lambdaUpdate()
                        .set(LearningRecord::getMoment, recordDTO.getMoment())
                        .set(finished, LearningRecord::getFinished, true)
                        .set(finished, LearningRecord::getFinishTime, recordDTO.getCommitTime())
                        .eq(LearningRecord::getId, old.getId())
                        .update();
                if(!success){
                    throw new DbException("更新数据库失败");
                }
                //清除redis缓存
                taskHandler.cleanRecordCache(recordDTO.getLessonId(),recordDTO.getSectionId());
            }
        }else{
            //处理考试
            LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
            record.setUserId(userId);
            record.setFinished(true);
            record.setFinishTime(recordDTO.getCommitTime());
            boolean success = this.save(record);
            if(!success){
                throw new DbException("新增考试记录失败");
            }
            finished = true;
        }

        //更新课表数据
        LearningLesson lesson = lessonService.getById(recordDTO.getLessonId());
        if(lesson==null){
            throw new BizIllegalException("课程不存在，无法更新");
        }
        //判断是否有新完成的小节
        boolean allLearned = false;
        if(finished){
            CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getId(), false, false);
            if(cInfo==null){
                throw new BizIllegalException("erro");
            }
            allLearned = lesson.getLearnedSections() + 1 >= cInfo.getSectionNum();
        }
        lessonService.lambdaUpdate()
                .set(lesson.getLearnedSections()==0,LearningLesson::getStatus,LessonStatus.LEARNING)
                .set(allLearned,LearningLesson::getStatus,LessonStatus.FINISHED)
                .set(!finished,LearningLesson::getLatestSectionId,recordDTO.getSectionId())
                .set(!finished,LearningLesson::getLatestLearnTime,recordDTO.getCommitTime())
                .setSql(finished,"learning_sections = learning_sections + 1")
                .update();
    }

    public void addLearningRecord1(LearningRecordFormDTO recordDTO) {
        //获取用户id，如果要新建记录表则需要用户id
        Long userId = UserContext.getUser();
        //创建一个boolean类型的变量，表示这个小节是否完成，用于后面更新课表使用
        boolean finished = false;
        //判断类型
        if(recordDTO.getSectionType()==SectionType.VIDEO){
            //视频类型
            finished = handleVideo(userId,recordDTO);
        }else{
            //考试类型
            finished = handleExam(userId,recordDTO);
        }
        //处理课程表
        handlession(finished,recordDTO);
    }

    private void handlession(boolean finished, LearningRecordFormDTO recordDTO) {
        LearningLesson lession = lessonService.getById(recordDTO.getLessonId());
        if(lession==null){
            throw new DbException("查询课表失败");
        }
        //判断是否学完课表中的全部课程
        boolean allLearned = false;
        if(finished){
            //查课程表，主要是获取这个课程中总共有多少个小节
            CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lession.getCourseId(), false, false);
            allLearned = lession.getLearnedSections() + 1 >= cInfo.getSectionNum();
        }
        //更新课表
        boolean success = lessonService.lambdaUpdate()
                .set(allLearned, LearningLesson::getStatus, 2)
                .set(!finished, LearningLesson::getLatestSectionId, recordDTO.getSectionId())
                .set(!finished, LearningLesson::getLatestLearnTime, recordDTO.getCommitTime())
                .last(finished, "set learned_sections = learned_sections+1")
                .eq(LearningLesson::getId, recordDTO.getLessonId())
                .update();
        if(!success){
            throw new DbException("更新课表失败");
        }
    }

    private boolean handleExam(Long userId, LearningRecordFormDTO recordDTO) {
        //是处理考试，则只需要新增记录表然后返回已学完就行
        LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
        record.setUserId(userId);
        record.setFinished(true);
        record.setFinishTime(recordDTO.getCommitTime());
        boolean success = this.save(record);
        if(!success){
            throw new DbException("新增记录表失败");
        }
        return true;
    }

    private boolean handleVideo(Long userId, LearningRecordFormDTO recordDTO) {
        //1、先从redis中查询学习记录
        LearningRecord old = taskHandler.readRecordCache(recordDTO.getLessonId(), recordDTO.getSectionId());
        if(old==null){
            //如果redis中没有，则需要从数据库中查询
            //redis中没有不代表就是第一次新增，数据库中也没有才可以确定是第一次新增
            old = this.lambdaQuery()
                    .eq(LearningRecord::getLessonId,recordDTO.getLessonId())
                    .eq(LearningRecord::getSectionId,recordDTO.getSectionId())
                    .one();
        }
        if(old==null){
            //2、如果数据库里面还是没有记录，就表示是第一次新增，将数据保存到数据库中
            //创建学习记录对象
            LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
            record.setUserId(userId);
            boolean success = this.save(record);
            if(!success){
                throw new DbException("新增学习记录失败");
            }
            //第一次新增，肯定该小节并未看完，返回false
            return false;
        }
        //3、不是第一次提交
        //判断该小节是否是第一次完成
        //第一次完成条件：之前的完成状态是未完成+提交进度*2>总时长
        boolean flag = ((!old.getFinished()) && (recordDTO.getMoment()*2 > recordDTO.getDuration()));
        if(!flag){
            //不是第一完成，将数据保存到redis和延迟队列中
            //使用之前实现的工具类taskHandler
            //封装需要发送给taskHandler的对象数据
            LearningRecord record = new LearningRecord();
            record.setLessonId(recordDTO.getLessonId());
            record.setSectionId(record.getSectionId());
            record.setMoment(recordDTO.getMoment());
            record.setId(old.getId());
            record.setFinished(old.getFinished());
            taskHandler.addLearningRecordTask(record);
            //不是第一次完成，所以返回值是false，表示肯定不需要更新课表
            return false;
        }
        //4、是第一次完成，需要更新数据库，不用更新redis，直接将redis删除就行
        boolean success = this.lambdaUpdate()
                .set(LearningRecord::getMoment, recordDTO.getMoment())
                .set(LearningRecord::getFinished, true)
                .set(LearningRecord::getFinishTime, recordDTO.getCommitTime())
                .eq(LearningRecord::getId, old.getId())
                .update();
        if(!success){
            throw new DbException("更新数据库失败");
        }
        //清楚redis缓存
        taskHandler.cleanRecordCache(recordDTO.getLessonId(), recordDTO.getSectionId());
        //小节第一次看完，需要返回true，用于表示需要更新课表
        return true;
    }

    private void handleLearningLessonChanges(LearningRecordFormDTO recordDTO, boolean finished) {
        //1.查询课表
        LearningLesson lesson = lessonService.getById(recordDTO.getLessonId());
        if(lesson == null){
            throw new BizIllegalException("课程不存在，无法更新数据");
        }
        //2.判断是否有新的完成的小节
        boolean allLearned = false;
        if(finished){
            //3.如果有新完成的小节，则需要查询数据库
            CourseFullInfoDTO cInfo= courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if(cInfo==null){
                throw new BizIllegalException("课程不存在，无法更新数据");
            }
            //4.比较课程是否全部学习完成：已学习小节 >= 课程总小节
            allLearned = lesson.getLearnedSections() + 1 >= cInfo.getSectionNum();
        }
        //5.更新课表
        lessonService.lambdaUpdate()
                .set(lesson.getLearnedSections()==0,LearningLesson::getStatus, LessonStatus.LEARNING)
                .set(allLearned,LearningLesson::getStatus,LessonStatus.FINISHED)
                .set(!finished,LearningLesson::getLatestSectionId,recordDTO.getSectionId())
                .set(!finished,LearningLesson::getLatestLearnTime,recordDTO.getCommitTime())
                .setSql(finished,"learned_section = learned_section + 1")
                .eq(LearningLesson::getId,recordDTO.getLessonId())
                .update();
    }

    private boolean handleExamRecord(Long userId, LearningRecordFormDTO recordDTO) {
        //1.转换DTO为PO
        LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
        //2.填充数据
        record.setUserId(userId);
        record.setFinishTime(recordDTO.getCommitTime());
        record.setFinished(true);
        //3.写入数据库
        boolean success = save(record);
        if(!success){
            throw new DbException("新增考试记录失败");
        }
        return true;
    }

    private boolean handleVideoRecord(Long userId, LearningRecordFormDTO recordDTO) {
        //1.查询旧的的学习记录
        LearningRecord old = queryOldRecord(recordDTO.getLessonId(),recordDTO.getSectionId());
        //2.判断是否存在
        if(old==null){
            //3.不存在则新增
            //3.1转换po
            LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
            //3.2填充数据
            record.setUserId(userId);
            //3.3写入数据库
            boolean success = save(record);
            if(!success){
                throw new DbRuntimeException("新增学习记录失败");
            }
            return false;
        }
        //4.存在则更新
        //4.1判断是否是第一次完成(视频进度超过50%就算是完成)
        boolean finished = !old.getFinished()&&recordDTO.getMoment()*2>= recordDTO.getDuration();
        if(!finished){
            //如果不是第一次完成，则只需要将更新数据写入到redis缓存即可
            LearningRecord record = new LearningRecord();
            record.setLessonId(recordDTO.getLessonId());
            record.setSectionId(recordDTO.getSectionId());
            record.setMoment(recordDTO.getMoment());
            record.setId(old.getId());
            record.setFinished(old.getFinished());
            taskHandler.addLearningRecordTask(record);
            return false;
        }
        //如果是第一次完成，则要更新数据库，同时清除redis缓存
        //4.2更新数据
        boolean success = lambdaUpdate()
                .set(LearningRecord::getMoment, recordDTO.getMoment())
                .set(finished, LearningRecord::getFinished, true)
                .set(finished, LearningRecord::getFinishTime, recordDTO.getCommitTime())
                .eq(LearningRecord::getId, old.getId())
                .update();
        if(!success){
            throw new DbException("更新学习记录失败");
        }
        //4.3清理缓存
        taskHandler.cleanRecordCache(recordDTO.getLessonId(),recordDTO.getSectionId());
        return true;
    }

    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        //1.查询缓存
        LearningRecord record = taskHandler.readRecordCache(lessonId, sectionId);
        //2.如果命中直接返回
        if(record!=null){
            return record;
        }
        //3.如果未命中，查询数据库
        record = lambdaQuery()
                .eq(LearningRecord::getLessonId,lessonId)
                .eq(LearningRecord::getSectionId,sectionId)
                .one();
        //4.将查到的结果写入缓存
        taskHandler.writeRecordCache(record);
        return record;
    }


}

